-- 服务器间远程调用模块
-- Author: Colocasia
-- Date: 2018-11-08

local skynet = require 'skynet';
local sc = require 'skynet.socketchannel';
local cluster = require 'skynet.cluster';
local timer = require 'timer';
local us = require 'us';
local class = require 'class';
local handler = require 'handler';
local table = require 'us.table';
local string = require 'us.string';
local log = require 'log';

---常量
local ENUM_SERVER = require 'server_const';

---静态变量
local config_name = skynet.getenv "cluster";
local server_id = skynet.getenv 'server_id';
local server_index = tonumber(skynet.getenv 'server_index');
local is_db_node = 1000 == server_index;

---节点配置
local config_cluster = {};
---节点列表
local cluster_list = {};

---服务器结构
local struct_server = {
    id      = '',                           -- 服务器唯一标示
    index   = 0,                            -- 服务器index
    addr    = '',                           -- 服务器地址
    port    = '',                           -- 服务器端口
    state   = ENUM_SERVER.STATE.OFFLINE,    -- 服务器状态
    name    = '',                           -- 服务器名称
}
---服务器实例
local server = {};
---服务器列表
local server_list = {};


---常用函数
---@type fun @获得当前物理机时间
local skynet_time = skynet.time;
---@type fun @clone
local table_clone = table.clone;
---@type fun @遍历表内容
local table_foreach = table.foreach;
---@type fun @合并表内容
local table_merge = table.merge;
---@type fun @拆分字符串
local string_split = string.split;
---@type fun @拆分字符串
local string_match = string.match;


local M = {}


---读取集群配置
function M:start()
    cluster.open(server_id);
    self:reload();
end


---尝试连接节点
local function try_connect(id)
    local data = config_cluster[id];
    if not data then return false end

    local addr, port = string_match(data, "([^:]+):(.*)$");
    local c = sc.channel {
        host = addr,
        port = tonumber(port),
        nodelay = true,
    }

    local succ, err = pcall(c.connect, c, true);
    if not succ then 
        log.warning("try to connect server '%s' faild!", id)
    end
    c:close()
    c = nil;
    return succ, err;
end


---读取集群配置
local function _loadconfig(config)
    config_cluster = config;
    if config_cluster then return end

    assert(config_name, "check config_cluster.lua");
    config_cluster = {};
    local f = assert(io.open(config_name))
    local source = f:read "*a"
    f:close()
    assert(load(source, "@"..config_name, "t", config_cluster))()
end


---重新载入集群配置
function M:reload(config)
    _loadconfig(config);
    log.dump(config_cluster, 'config_cluster');
    cluster.reload(config_cluster);
    if is_db_node then
        self:init_server_list();
    else
        self:register_server();
    end
end


---获取服务器列表
function M:get_server_list()
    return table_clone(server_list);
end


---获取服务器节点
function M:get_server(server)
    return table_clone(server_list[server] or {});
end


-- master ---------------------------------------------------------------------
---初始化服务器列表
function M:init_server_list()
    assert(is_db_node, "only db node can init server_list");
    table_foreach(config_cluster, function(data, id)
        local addr, port = string_match(data, "([^:]+):(.*)$");
        local s = table_merge({table_clone(struct_server), {
            id      = id,
            index   = server_id == id and server_index or nil,
            addr    = addr,
            port    = tonumber(port),
            state   = server_id == id and ENUM_SERVER.STATE.ONLINE or ENUM_SERVER.STATE.OFFLINE,
            name    = server_id == id and id or nil,
        }});
        server_list[s.id] = s;

        if server_id == s.id then
            server = s;
        end
    end)
    log.dump(server_list, 'server_list');
end


---注册服务器节点
function M:register(server)
    assert(is_db_node, "only db node can register server to server_list");
    assert(server_list, "register must after init server_list");

    local server_id = server.id;
    if not server_list[server_id] then return false end
    
    local s = server_list[server_id]
    s.index = server.index;
    s.state = server.state;
    s.name = server.name;
    self:push_server_list()
    
    return true;
end


---推送服务器节点
function M:push_server_list()
    assert(is_db_node, "only db node can push server_list");
    log.dump(server_list, 'server_list');

    table_foreach(server_list, function(server, id)
        if id == server_id then return end
        if ENUM_SERVER.STATE.OFFLINE == server.state then return end
        handler:exc_server_cmd(id, "recive_server_list", server_list);
    end)
end
-------------------------------------------------------------------------------


-- slave ----------------------------------------------------------------------
---注册服务器节点
function M:register_server()
    -- 此处应阻塞等待连接DB节点成功
    while not try_connect("DB") do
        skynet.sleep(500);
    end

    server = table_merge({table_clone(struct_server), {
        id = server.id or server_id,
        index = server.index or server_index,
        state = ENUM_SERVER.STATE.ONLINE,
        name  = server.name or server_id,
    }});

    handler:exc_server_cmd("DB", "register", server);
end


---接收推送的服务器节点
function M:recive_server_list(server_list)
    server_list = server_list
    -- log.dump(server_list, 'server_list');
end
-------------------------------------------------------------------------------

return M
